生成魔咒
Time Limit: 10 Sec Memory Limit: 128 MB
Description
魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1、2 拼凑起来形成一个魔咒串 [1,2]。
一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒。
例如 S=[1,2,1] 时,它的生成魔咒有 [1]、[2]、[1,2]、[2,1]、[1,2,1] 五种。
S=[1,1,1] 时,它的生成魔咒有 [1]、[1,1]、[1,1,1] 三种。
最初 S 为空串。共进行 n 次操作,每次操作是在 S 的结尾加入一个魔咒字符。
每次操作后都需要求出,当前的魔咒串 S 共有多少种生成魔咒。
第一行一个整数 n。
第二行 n 个数,第 i 个数表示第 i 次操作加入的魔咒字符。
Output
输出 n 行,每行一个数。第 i 行的数表示第 i 次操作后 S 的生成魔咒数量
7
1 2 3 3 3 1 2
Sample Output
1
3
6
9
12
17
22
HINT
1≤n≤100000
Main idea
询问在加入每一个字符后当前有多少个本质不同的子串。
Solution
直接用SAM,根据SAM的性质,每次增多的子串个数就是len[New] - len[fa[New]] 。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <bits/stdc++.h> using namespace std;typedef long long s64;const int ONE = 400005 ;const int INF = 2147483640 ;int n,x;s64 Ans; int get () { int res=1 ,Q=1 ;char c; while ( (c=getchar ())<48 || c>57 ) if (c=='-' )Q=-1 ; res=c-48 ; while ( (c=getchar ())>=48 && c<=57 ) res=res*10 +c-48 ; return res*Q; } struct SAM { map <int , int > a[ONE]; int len[ONE], fa[ONE]; int last, cnt; SAM () {last = cnt = 1 ;} void Add (int c) { int x = last, New = last = ++cnt; len[New] = len[x] + 1 ; while (x && !a[x][c]) a[x][c] = New, x = fa[x]; if (!x) {fa[New] = 1 ; Ans += len[New] - len[fa[New]]; return ;} int q = a[x][c]; if (len[x] + 1 == len[q]) fa[New] = q; else { int Nq = ++cnt; len[Nq] = len[x] + 1 ; a[Nq] = a[q]; fa[Nq] = fa[q]; fa[New] = fa[q] = Nq; while (a[x][c] == q) a[x][c] = Nq, x = fa[x]; } Ans += len[New] - len[fa[New]]; } }S; int main () { n = get (); while (n--) { x = get (); S.Add (x); printf ("%lld\n" , Ans); } }